package cn.tedu.carehome.foundation.filter;


import cn.tedu.carehome.foundation.security.LoginPrincipal;
import cn.tedu.carehome.foundation.web.JsonResult;
import cn.tedu.carehome.foundation.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * <p>处理JWT的过滤器类</p>
 *
 * <p>此过滤器类的主要职责</p>
 * <ul>
 *     <li>接收客户端可能提交的JWT</li>
 *     <li>尝试解析客户端提交的JWT</li>
 *     <li>将解析得到的结果存入到SecurityContext中</li>
 * </ul>
 *
 * @author java@tedu.cn
 * @version 0.0.1
 */


@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Value("${csmall.jwt.secret-key}")
    private String secretKey;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //清楚SecurityContextHolder中的数据
        SecurityContextHolder.clearContext();

        //根据业内惯用做法，客户端应该将JWT保存在请求头（Request Header）中的名为Authorization的属性中
        String jwt = request.getHeader("Authorization");
        log.debug("尝试接收客户端携带的JWT数据JWT：{}", jwt);
        if (!StringUtils.hasText(jwt) || jwt.length() < 113) {
            //直接放行
            filterChain.doFilter(request, response);
            //【重要】不执行接下来代码
            return;
        }
        // 【已解决】问题1：secretKey不应该被反复定义
        // 【已解决】问题2：解析过程中可能抛出异常
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();
        } catch (SignatureException e) {
            response.setContentType("application/json;charset=utf-8");
            String message = "非法访问！！！";
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_SIGNATURE, message);
            PrintWriter pw = response.getWriter();
            pw.println(JSON.toJSONString(jsonResult));
            pw.close();
            return;
        } catch (MalformedJwtException e) {
            response.setContentType("application/json;charset=utf-8");
            String message = "非法访问！！！";
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_MALFORMED, message);
            PrintWriter pw = response.getWriter();
            pw.println(JSON.toJSONString(jsonResult));
            pw.close();
            return;
        } catch (ExpiredJwtException e) {
            response.setContentType("application/json;charset=utf-8");
            String message = "您的登录信息已过期，请重新登录";
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_EXPIRED, message);
            PrintWriter pw = response.getWriter();
            pw.println(JSON.toJSONString(jsonResult));
            pw.close();
            return;
        }
        Long id = claims.get("id", Long.class);
        String username = claims.get("username", String.class);
        String authoritiesJsonString = claims.get("authoritiesJsonString", String.class);
        System.out.println("从JWT中解析得到的管理员ID：{}" + id);
        System.out.println("从JWT中解析得到的管理员用户名:{}" + username);
        System.out.println("从JWT中解析得到的管理员权限：{}" + authoritiesJsonString);

        // 将JSON格式的权限列表转换成Authentication需要的类型（Collection<GrantedAuthority>）
        List<SimpleGrantedAuthority> authorities =
                JSON.parseArray(authoritiesJsonString, SimpleGrantedAuthority.class);
        // 基于解析JWT的结果创建认证信息
        //问题3：是否应该使用“用户名”这么简单的数据作为“当事人”
        //问题4：如何得到当前管理员的权限
        LoginPrincipal loginPrincipal = new LoginPrincipal(); // 使用用户名作为当事人数据（临时）
        loginPrincipal.setId(id);
        loginPrincipal.setUsername(username);
        Object credentials = null; // 应该为null
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                loginPrincipal, credentials, authorities);

        // 将认证信息存入到SecurityContext中
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authentication);

        // 过滤器链继承向后执行，即：放行
        // 如果没有执行以下代码，表示“阻止”，即此请求的处理过程到此结束，在浏览器中将显示一片空白
        filterChain.doFilter(request, response);
    }
}
